Skip to content

fix: Login page not accessible after disabling plugin.#94

Open
navidabdi wants to merge 4 commits into
masterfrom
fix/restore-login-on-deactivation
Open

fix: Login page not accessible after disabling plugin.#94
navidabdi wants to merge 4 commits into
masterfrom
fix/restore-login-on-deactivation

Conversation

@navidabdi

@navidabdi navidabdi commented May 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Deactivating core-standards previously left a .htaccess block in place that returned 403 Forbidden for /wp-login.php and rewrote /login.php to it, so the login page was unreachable until the plugin was reactivated or the rules were manually removed.
  • Schema::deactivate() now removes every core-standards:* block the plugin previously wrote to the root and uploads .htaccess (including the legacy core-standards:security marker addressed by 95aefe6), restoring the site to its pre-plugin state.
  • Added a marker-based removeBlockFromFile() helper so removal works regardless of what content an older plugin version may have written between the # BEGIN / # END markers.

Test plan

  • Activate the plugin; confirm grep -c "core-standards:" .htaccess returns 5 and /login.php works while /wp-login.php returns 403.
  • Deactivate the plugin; confirm grep -c "core-standards:" .htaccess returns 0, uploads .htaccess also has 0 matches, and /wp-login.php loads the login form (no 403).
  • Reactivate the plugin; confirm the blocks are re-added and /login.php works again.
  • Upgrade-safety: with the plugin active, inject the legacy # BEGIN core-standards:security ... # END core-standards:security block manually, then deactivate — both legacy and current blocks should be removed.
  • No-op safety: deactivate on a site whose .htaccess is missing or already clean — no errors, no file written.

On activation the plugin writes a .htaccess block that blocks direct
access to /wp-login.php with 403 and rewrites /login.php to it. The
deactivation hook never removed those rules, so deactivating the
plugin left the site unable to reach the login page at all.

Schema::deactivate() now removes every core-standards:* block the
plugin previously added to the root and uploads .htaccess files
(including the legacy core-standards:security marker), via a new
marker-based removeBlockFromFile() helper.
@navidabdi navidabdi changed the title Fixed login page inaccessible after plugin deactivation fix: login page inaccessible after plugin deactivation May 18, 2026
navidabdi added 2 commits May 18, 2026 11:51
Stands up the first automated test coverage for this plugin and locks
in the deactivation/login fix from this PR as a regression test.

Stack:
- @wordpress/env boots a Dockerized WP + Apache + MariaDB sandbox so
  the plugin's .htaccess rules execute exactly as in production.
- Playwright drives the tests; HTTP-only assertions use request
  context (no browser launch needed, fast in CI).
- GitHub Actions workflow runs the suite on pull_request and pushes
  to master, uploading the Playwright report on failure.

Files:
- .wp-env.json (ports 8989/8990 to avoid colliding with other local
  wp-env instances)
- playwright.config.js
- tests/e2e/deactivation-restores-login.spec.js (7 test cases covering
  active state, deactivation cleanup, reactivation, and the legacy
  core-standards:security marker addressed by 95aefe6)
- tests/e2e/helpers/wp-env.js (shared primitives for future specs)
- tests/e2e/fixtures/legacy-security-block.htaccess
- tests/e2e/README.md (contributor docs)
- .github/workflows/e2e.yml
- package.json: env:start / env:stop / env:clean / test:e2e scripts
  and devDependencies

Verified locally: all 7 tests pass with the fix in place, and test 4
("after deactivation: /wp-login.php is reachable again") fails when
the fix is temporarily reverted, proving the suite guards the fix.
…t dir.

When wp-env's `plugins: ["."]` is used, the activation slug is derived
from the host directory's basename. On GitHub Actions the repo is
checked out into `wordpress-core-standards/`, so the plugin slug became
`wordpress-core-standards` and the hardcoded `wp plugin activate
core-standards` in the test helper failed.

Switching to `mappings` lets us control the destination path inside
the container, so the slug is always `core-standards` regardless of
where the repo lives on the host. Activation is handled by the test's
beforeAll (it already was), so no behavior change locally.
@navidabdi navidabdi requested a review from sun May 18, 2026 10:34

@sun sun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these rules are crucial for security and stability of the (production) site, we can’t remove them.

We can consider to hook their removal into wp plugin uninstall.

For the deactivate hook, I would suggest to only comment out the line that rejects access to the regular login front controller. Re-enabling the plugin will revert the change.

…n deactivate.

The security/stability rules the plugin writes to .htaccess are needed
in production even while the plugin is temporarily deactivated.
Removing them on every deactivation was too aggressive (per PR review).

New behavior:
- deactivate(): comments out only the line that blocks /wp-login.php,
  so the login page is reachable while the plugin is inactive. All
  other rules (xmlrpc, .git, wp-includes/*.php, security headers,
  fast 404, uploads no-exec) remain in effect. Re-activation rewrites
  the security-files block from the template, restoring the line.
- uninstall(): now performs the full .htaccess cleanup of every
  core-standards:* block (including the legacy core-standards:security
  marker) — fires via register_uninstall_hook when the plugin is
  permanently removed.

Also fixed a regex bug in removeBlockFromFile: the previous `\b` after
`core-standards:security` was satisfied by a hyphen, so removing the
legacy marker could also strip `:security-files` / `:security-headers`.
The marker is now anchored at end-of-line.

E2E suite updated accordingly:
- "after deactivation" tests now assert the security blocks REMAIN and
  only the wp-login.php rule line is commented out.
- A new "uninstall" test invokes `wp plugin uninstall --skip-delete` to
  fire the uninstall hook (skip-delete keeps the bind-mounted plugin
  files intact) and verifies every block, including the legacy marker,
  is removed.
@navidabdi navidabdi requested a review from sun May 26, 2026 08:47
@navidabdi navidabdi requested review from fabianmarz and removed request for sun July 6, 2026 07:49

@sun sun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

Comment thread src/Schema.php
Comment on lines +58 to +63
// The plugin may be deactivated temporarily; the production security
// and stability rules in .htaccess must stay in place. Only the rule
// that blocks direct access to /wp-login.php is disabled, so the
// login page remains reachable while the plugin is inactive.
// Re-activation rewrites the security-files block from the template
// and naturally restores the line.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// The plugin may be deactivated temporarily; the production security
// and stability rules in .htaccess must stay in place. Only the rule
// that blocks direct access to /wp-login.php is disabled, so the
// login page remains reachable while the plugin is inactive.
// Re-activation rewrites the security-files block from the template
// and naturally restores the line.
// When temporarily disabling the plugin, disable the rule that
// blocks direct access to /wp-login.php, so admins can log in.
// Reenabling the plugin rewrites all templates, so the line is
// restored.

@sun sun changed the title fix: login page inaccessible after plugin deactivation fix: Login page not accessible after disabling plugin. Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants